﻿using System;
using System.Collections.Generic;
using System.Linq;
using ExtensionHelper;


namespace Expression
{
    using DynamicExpresso;
    using Interface;
    class AlgorithmProcess<T>: IAlgorithmBehaviours
    {
        #region 私有变量

        /// <summary>
        /// 具体的执行对象
        /// </summary>
        private Interpreter target = null;

        /// <summary>
        /// 错误信息
        /// </summary>
        private List<string> errors = null;

        /// <summary>
        /// 是否存在异常
        /// </summary>
        private bool HasException = false;

        /// <summary>
        /// 表达式使用的数据源
        /// </summary>
        private List<T> DataSource { get; set; }

        /// <summary>
        /// 当前正在执行的算法
        /// </summary> 
        private IAlgorithm currentAlgorithm { get; set; }
        #endregion

        #region 构造

        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        /// <param name="algorithm"></param>
        public AlgorithmProcess(List<T> data,IAlgorithm algorithm)
        {
            target = new Interpreter();
            errors = new List<string>();
            RegisterFunctions();
            RegisterSystemParms();
            this.DataSource = data;
            this.currentAlgorithm = algorithm;
        }

        #endregion

        #region 私有方法

        /// <summary>
        /// 创建一个新的系统变量名称
        /// </summary>
        /// <returns></returns>
        private string NewArgName()
        {
            string res = "sys_arg_";
            char[] chars = new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
            for (int i = 0; i < 3; i++)
            {
                Random r = new Random();
                res += chars[r.Next(1, 26)] + r.Next(1, 9).ToString() + chars[r.Next(1, 26)];
            }
            return res;
        }

 

        /// <summary>
        /// 注册公用方法
        /// </summary>
        private void RegisterFunctions()
        {
            //2^3 = 8
            Func<double, double, double> Pow = (x, y) => Math.Pow(x, y);
            //1,2 = 2
            Func<double, double, double> Max = (x, y) => Math.Max(x, y);
            //1,2 = 1
            Func<double, double, double> Min = (x, y) => Math.Min(x, y);
            // 1 = 1, -1 = 1
            Func<double, double> Abs = (x) => Math.Abs(x);
            //loge log(10) = 2.302585092994046
            Func<double, double> Log = (x) => Math.Log(x);
            //log10  log10(100) =2 
            Func<double, double> Log10 = (x) => Math.Log10(x);
            //log2 log2(2) =1
            Func<double, double> Log2 = (x) => Math.Log2(x);

            Func<string, string, double> Sum = (col, filter) => {

                try
                {
                    //先根据条件缩小范围
                    var filter_delegate = target.ParseAsDelegate<Func<T, bool>>(filter, "d");
                    var sub_dataSource = DataSource.Where(filter_delegate);
                    //求和
                    var condition = target.ParseAsDelegate<Func<T, double>>($"{col}", "d");
                    var sum = sub_dataSource.Sum(condition); 
                    return sum;
                }
                catch (Exception ex)
                {
                    HasException = true;
                    errors.Add(ex.Message);
                    return 0;
                }
            };

            Func<double, string, string,double> Like = (data, column,filter) => {
                //like(17.3,column)
                //严格匹配
                if (column.RegexIsMatch(@"^\w+$"))
                {
                    try
                    {
                        //先缩小范围
                        var filter_delegate = target.ParseAsDelegate<Func<T, bool>>(filter, "d");
                        var sub_dataSource = DataSource.Where(filter_delegate);
                        //找到差中，最小的值
                        var condition = target.ParseAsDelegate<Func<T, double>>($" Math.Abs({column} - {data}) ", "d");
                        var sub_min = sub_dataSource.Min(condition); 

                        var like = target.ParseAsDelegate<Func<T, bool>>($" Math.Abs({column} - {sub_min}) == {data} ", "d");
                        var single_obj = sub_dataSource.Where(like).FirstOrDefault();

                        var temp_var_1 = NewArgName();
                        target.SetVariable(temp_var_1, single_obj);
                        var result = target.Eval($"{temp_var_1}.{column} + 0"); 
                        return result.ToString().ToDouble();
                    }
                    catch (Exception ex)
                    {
                        HasException = true;
                        errors.Add(ex.Message);
                        return 0;
                    }                    
                   
                }
                return 0;
            };
            Func<Point, Point, double> Ith1 = (pt1, pt2) =>
            {
                // y = ax +b 
                if (pt1.X == pt2.X)
                {
                    HasException = true;
                    errors.Add("Ith1算法中，两个点的x坐标不能一样");
                    return 0;
                }
                return pt1.Y - (pt2.Y - pt1.Y) / (pt2.X - pt1.X) * pt1.X;
            };
            //系统内置方法
            target.SetFunction("pow", Pow);
            target.SetFunction("max", Max); 
            target.SetFunction("min", Min);
            target.SetFunction("abs", Abs);
            target.SetFunction("log", Log);
            target.SetFunction("log10", Log10);
            target.SetFunction("log2", Log2);


            //自定义方法
            target.SetFunction("like", Like);
            target.SetFunction("ith1", Ith1);
            target.SetFunction("sum", Sum);
        }

        /// <summary>
        /// 注册公用系统参数
        /// </summary>
        private void RegisterSystemParms()
        {
            target.SetVariable("system_arg_pi", Math.PI);


        }

        #endregion 

        #region 公共方法


        /*
         * 第一步：接受参数，对参数转型，将参数添加到参数列表 【注意：用户参数命名格式为： u_arg_[number]】 
         * 
         * 
         * 第二步：根据查询条件，找到所有需要计算的参数，并对查询结果，赋值给参数 
         * 
         * 
         * 第三步：根据计算表达式，计算出结果
         */

        /// <summary>
        /// 第一步：接受参数，对参数转型，将参数添加到参数列表
        /// </summary>
        /// <param name="args"></param>
        public bool LoadParams(List<IArg> args)
        {
            try
            {
                if (args == null)
                    throw new Exception("算法参数不能为空");
                args.ForEach((a) =>
                {
                    ArgUser arg = a as ArgUser;
                    arg.Converter(arg, this);
                });
            }
            catch (Exception ex)
            {
                errors.Add(ex.Message);
                return false;
            }
            return true;

        }


        /// <summary>
        /// 根据查询条件执行查询并赋值
        /// </summary>
        /// <param name="querys">查询条件</param>
        /// <returns>如果执行没有异常，则返回true</returns>
        public bool AssignFromConditionals(List<IConditional> querys)
        {
            bool result = true;
            if (querys != null && querys.Count > 0)
            {
               
                foreach (var q in querys)
                {
                    result = AssignFromConditional(q);
                    if (result == false)
                    {
                        return result;
                    }
                } 
            }
            return result;
        }

        /// <summary>
        ///  第二步：根据查询条件，找到所有需要计算的参数，并对查询结果，赋值给参数 
        /// </summary>
        /// <param name="query">查询条件</param>
        /// <returns>如果执行没有异常，则返回true</returns>
        public bool AssignFromConditional(IConditional query)
        {
            /*
              "assigns": [ "u_arg_3 = current", "u_arg_4 = MDPCurrent" ],
              "condition": "d.Power == u_arg_2 && d.Temperature == u_arg_0"
             */
            if (HasException)
                return false ;
            try
            {
                if (query != null)
                {
                    Dictionary<string, string> vars = new Dictionary<string, string>();
                    query.Assign.ForEach(v =>
                    {
                        string[] temp = v.Split('=');
                        vars.Add(temp[0].Trim(), temp[1].Trim());
                    });
                    //约定 遍历过程中的 形参用 d
                    var whereFunction = target.ParseAsDelegate<Func<T, bool>>(query.Condition, "d");
                    //做数据查询
                    var d = DataSource.Where(whereFunction).FirstOrDefault();
                    //异常判断
                    if (d == null)
                    {

                        HasException = true;
                        errors.Add($"在执行[{query.Condition}]的过程中，没有查询到数据");
                        return false;
                    }
                    var temp_var = NewArgName();
                    target.SetVariable(temp_var, d);
                    //将对应的数据列存放到指定的变量中
                    foreach (var item in vars)
                    {
                        target.SetVariable(item.Key, target.Eval($"{temp_var}.{item.Value}"));
                    }
                }
                //else
                //{
                //    HasException = true;
                //    errors.Add($"查询条件为空");
                //    return false;
                //}
            }
            catch (Exception ex)
            { 
                HasException = true;
                errors.Add(ex.Message);
                return false;
            }
            return true;
        } 

        /// <summary>
        /// 第三步：根据计算表达式，计算出结果
        /// </summary>
        /// <returns></returns>
        public double Execute()
        {
            try
            {
               
                LoadParams(currentAlgorithm.UserParameters);
                AssignFromConditionals(currentAlgorithm.Conditions);
                if (HasException)
                {
                    return 0;
                }
                if (currentAlgorithm.Expression.IsNullOrEmpty())
                    throw new Exception("算法表达式不能为空");
                return target.Eval(currentAlgorithm.Expression).ToString().ToDouble();
            }
            catch (Exception ex)
            { 
                HasException = true;
                errors.Add(ex.Message);
            } 
            return 0;
        }

        /// <summary>
        /// 接受表达式，并计算表达式
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public double Eval(string exp)
        {
            return target.Eval(exp).ToString().ToDouble();
        }

        #endregion

        #region IExpression 接口实现

        public void AddParameters(IArg arg)
        {
            ArgUser argUser = arg as ArgUser;
            argUser.Converter(argUser, this); 
        }
 
        public List<string> GetLastError()
        {
            return errors;
        }

        public void AddParameters<T1>(string name, T1 val)
        {
            target.SetVariable<T1>(name, val);
        }

        #endregion
    }

}
